<!DOCTYPE html>
<html lang="en">
<head>
  <title>egg - Born to build better enterprise frameworks and apps</title>
  <meta charset="utf-8">
  <meta name="description" content="index.description">
  <meta http-equiv="X-UA-Compatible" content="IE=Edge,chrome=1">
  <meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1, user-scalable=no">
  <link rel="icon" href="/images/favicon.png" type="image/x-icon">
  <link rel="stylesheet" href="https://cdn.jsdelivr.net/docsearch.js/2/docsearch.min.css" />
<link rel="stylesheet" href="/css/index.css">

    <script>
    !function(t,e,a,r,c){t.TracertCmdCache=t.TracertCmdCache||[],t[c]=window[c]||
      {_isInit:!0,call:function(){t.TracertCmdCache.push(arguments)},
      start:function(t){this.call('start',t)}},t[c].l=new Date;
      var n=e.createElement(a),s=e.getElementsByTagName(a)[0];
      n.async=!0,n.src=r,s.parentNode.insertBefore(n,s)}
    (window,document,'script','https://tracert.alipay.com/tracert.js','Tracert');
      Tracert.start({
        plugins: [ 'BucName' ],
        spmAPos: 'a454',
        spmBPos: 'b4893',
      });
    </script>
  
</head>
<body>
  <div class="nav" >
  <header>
    <a href="/en/" class="nav-logo leftpadding" alt="egg"><img src="https://zos.alipayobjects.com/rmsportal/VTcUYAaoKqXyHJbLAPyF.svg"></a>
    <ul class="nav-item">
      <li>
        <form id="search-form">
          <input type="text" id="search-query" class="search-query st-default-search-input">
        </form>
      </li>
      <li><a href="/en/intro/" alt="Guide">Guide</a></li><li><a href="/api/" alt="API">API</a></li><li><a href="/en/tutorials/index.html" alt="Tutorials">Tutorials</a></li><li><a href="https://github.com/search?q=topic%3Aegg-plugin&type=Repositories" alt="Plugins">Plugins</a></li><li><a href="https://github.com/eggjs/egg/releases" alt="Release">Release</a></li>
      
      
        <li class="translations">
          <a class="nav-link">中文文档</a>
          <span class="arrow"></span><ul id="dropdownContent" class="dropdown-content"><li><a id="en" href="/en/tutorials/socketio.html" style="color: #22ab28">English</a></li><li><a id="zh-cn" href="/zh-cn/tutorials/socketio.html" >中文</a></li></ul>
        </li>
      
      <li><iframe src="https://ghbtns.com/github-btn.html?user=eggjs&repo=egg&type=star&count=true" frameborder="0" scrolling="0" width="150px" height="20px"></iframe></li>
    </ul>
    <a id="mobileTrigger" href="#" class="mobile-trigger">
      <ul>
        <li></li>
        <li></li>
        <li></li>
      </ul>
    </a>
  </header>
</div>
  <div id="container" class="container">
    <div class="page-main">
  <article class="markdown-body">
    <h1></h1>
    <h2 id="title-socketio"><a class="markdown-anchor" href="#title-socketio">#</a> title: Socket.IO</h2>
<p>** Socket.IO ** is a real-time application framework based on Node.js, which has a wide range of applications including instant messaging, notification and message push, real-time analysis and other scenarios.</p>
<p>WebSocket originated from the growing demand for real-time communication in web development, compared with http-based polling, which greatly saves network bandwidth and reduces server performance consumption. <a href="https://socket.io" target="_blank" rel="noopener">Socket.IO</a> supports both websockets and polling. The data transmission method is compatible with the browser and does not support the communication requirements under the WebSocket scenario.</p>
<p>The framework provides the <a href="https://github.com/eggjs/egg-socket.io" target="_blank" rel="noopener">egg-socket.io</a> plugin with the following development rules added:</p>
<ul>
<li>namespace: define the namespace by means of configuration
 - middleware: establish / disconnect every socket connection, preprocess every message / data transfer
 - controller: response socket.io event
 - router: unify the processing configuration of socket.io event and frame routing</li>
</ul>
<h2 id="install-egg-socketio"><a class="markdown-anchor" href="#install-egg-socketio">#</a> install egg-socket.io</h2>
<h3 id="installation"><a class="markdown-anchor" href="#installation">#</a> Installation</h3>
<figure class="highlight bash"><table><tr><td class="code"><pre><span class="line">$ npm i egg-socket.io --save</span><br></pre></td></tr></table></figure>
<p>** Enable the plugin: **</p>
<figure class="highlight js"><table><tr><td class="code"><pre><span class="line"><span class="comment">// &#123;app_root&#125; /config/plugin.js</span></span><br><span class="line">exports.io = &#123;</span><br><span class="line">  enable: <span class="literal">true</span>,</span><br><span class="line">  package: <span class="string">'egg-socket.io'</span></span><br><span class="line">&#125;;</span><br></pre></td></tr></table></figure>
<h3 id="configuration"><a class="markdown-anchor" href="#configuration">#</a> Configuration</h3>
<figure class="highlight js"><table><tr><td class="code"><pre><span class="line"><span class="comment">// &#123;app_root&#125; / config / config. $ &#123;env&#125; .js</span></span><br><span class="line">exports.io = &#123;</span><br><span class="line">  init: &#123;&#125;, <span class="comment">// passed to engine.io</span></span><br><span class="line">  namespace: &#123;</span><br><span class="line">    <span class="string">'/'</span>: &#123;</span><br><span class="line">      connectionMiddleware: [],</span><br><span class="line">      packetMiddleware: []</span><br><span class="line">    &#125;,</span><br><span class="line">    <span class="string">'/ example'</span>: &#123;</span><br><span class="line">      connectionMiddleware: [],</span><br><span class="line">      packetMiddleware: []</span><br><span class="line">    &#125;</span><br><span class="line">  &#125;</span><br><span class="line">&#125;;</span><br></pre></td></tr></table></figure>
<blockquote>
<p>Namespaces are <code>/</code> and <code>/ example</code>, not<code>example</code></p>
</blockquote>
<p>** uws: **</p>
<p>If you want to use <a href="https://github.com/uWebSockets/uWebSockets" target="_blank" rel="noopener">uws</a> instead of the default <code>us</code> you can do the following configuration</p>
<figure class="highlight js"><table><tr><td class="code"><pre><span class="line"><span class="comment">// &#123;app_root&#125; / config / config. $ &#123;env&#125; .js</span></span><br><span class="line">exports.io = &#123;</span><br><span class="line">  init: &#123; <span class="attr">wsEngine</span>: <span class="string">'uws'</span> &#125; <span class="comment">// default: us</span></span><br><span class="line">&#125;;</span><br></pre></td></tr></table></figure>
<p>** redis: **</p>
<p><a href="https://github.com/eggjs/egg-socket.io" target="_blank" rel="noopener">egg-socket.io</a> has built-in redis support via <code>socket.io-redis</code>. In cluster mode, the use of redis can make it relatively simple to achieve information sharing of clients/rooms and so on</p>
<figure class="highlight js"><table><tr><td class="code"><pre><span class="line"><span class="comment">// &#123;app_root&#125; / config / config. $ &#123;env&#125; .js</span></span><br><span class="line">exports.io = &#123;</span><br><span class="line">  redis: &#123;</span><br><span class="line">    host: &#123;redis server host&#125;</span><br><span class="line">    port: &#123;redis server prot&#125;,</span><br><span class="line">    auth_pass: &#123;redis server password&#125;,</span><br><span class="line">    db: <span class="number">0</span>,</span><br><span class="line">  &#125;,</span><br><span class="line">&#125;;</span><br></pre></td></tr></table></figure>
<blockquote>
<p>When <code>redis</code> is turned on, the program tries to connect to the redis server at startup
Here <code>redis</code> is only used to store connection instance information, see <a href="https://socket.io/docs/server-api/#server-adapter-value" target="_blank" rel="noopener"># server.adapter</a></p>
</blockquote>
<p><strong>note:</strong>
If the project also uses the <code>egg-redis</code>, please configure it separately. Do not share it.</p>
<h3 id="deployment"><a class="markdown-anchor" href="#deployment">#</a> Deployment</h3>
<p>If the framework is started in cluster mode, the socket.io protocol implementation needs sticky feature support, otherwise it will not work in multi-process mode.</p>
<p>Due to the design of socket.io, a multi-process server must be in the sticky working mode. As a result, you need the need to pass parameter <code>--sticky</code> when starting the cluster.</p>
<p>Modify the <code>npm scripts</code> script in<code>package.json</code>:</p>
<figure class="highlight js"><table><tr><td class="code"><pre><span class="line">&#123;</span><br><span class="line">  <span class="string">"scripts"</span>: &#123;</span><br><span class="line">    <span class="string">"dev"</span>: <span class="string">"egg-bin dev --sticky"</span>,</span><br><span class="line">    <span class="string">"start"</span>: <span class="string">"egg-scripts start --sticky"</span></span><br><span class="line">  &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>
<p>** Nginx configuration **</p>
<figure class="highlight plain"><table><tr><td class="code"><pre><span class="line">location / &#123;</span><br><span class="line">  proxy_set_header Upgrade $ http_upgrade;</span><br><span class="line">  proxy_set_header Connection &quot;upgrade&quot;;</span><br><span class="line">  proxy_set_header X-Forwarded-For $ proxy_add_x_forwarded_for;</span><br><span class="line">  proxy_set_header Host $ host;</span><br><span class="line">  proxy_pass http://127.0.0.1:7001;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>
<h2 id="using-egg-socketio"><a class="markdown-anchor" href="#using-egg-socketio">#</a> Using egg-socket.io</h2>
<p>The directory structure of project which has enabled the <a href="https://github.com/eggjs/egg-socket.io" target="_blank" rel="noopener">egg-socket.io</a> is as follows:</p>
<figure class="highlight plain"><table><tr><td class="code"><pre><span class="line">chat</span><br><span class="line">├── app</span><br><span class="line">│ ├── extend</span><br><span class="line">│ │ └── helper.js</span><br><span class="line">│ ├── io</span><br><span class="line">│ │ ├── controller</span><br><span class="line">│ │ │ └── default.js</span><br><span class="line">│ │ └── middleware</span><br><span class="line">│ │ ├── connection.js</span><br><span class="line">│ │ └── packet.js</span><br><span class="line">│ └── router.js</span><br><span class="line">├── config</span><br><span class="line">└── package.json</span><br></pre></td></tr></table></figure>
<blockquote>
<p>Note: The corresponding files are in the app / io directory</p>
</blockquote>
<h3 id="middleware"><a class="markdown-anchor" href="#middleware">#</a> Middleware</h3>
<p>Middleware has the following two scenarios:</p>
<ul>
<li>Connection</li>
<li>Packet</li>
</ul>
<p>It is configured in each namespace, respectively, according to the scenarios given above.</p>
<p><strong>note:</strong></p>
<p>If we enable the framework middleware, you will find the following directory in the project:</p>
<ul>
<li><code>app / middleware</code>: framework middleware</li>
<li><code>app / io / middleware</code>: plugin middleware</li>
</ul>
<p>the difference:</p>
<ul>
<li>Framework middleware is based on http model design to handle http requests.</li>
<li>Plugin middleware based socket model design, processing socket.io request.</li>
</ul>
<p>Although the framework tries to unify the style through plugins, it is important to note that their usage scenarios are different. For details, please see: <a href="https://github.com/eggjs/egg/issues/1416" target="_blank" rel="noopener"># 1416</a></p>
<h4 id="connection"><a class="markdown-anchor" href="#connection">#</a> Connection</h4>
<p>Fires when each client connects or quits. Therefore, we usually perform authorization authentication at this step, and deal with the failed clients.</p>
<figure class="highlight js"><table><tr><td class="code"><pre><span class="line"><span class="comment">// &#123;app_root&#125; /app/io/middleware/connection.js</span></span><br><span class="line"><span class="built_in">module</span>.exports = <span class="function"><span class="params">app</span> =&gt;</span> &#123;</span><br><span class="line">  <span class="keyword">return</span> <span class="keyword">async</span> (ctx, next) =&gt; &#123;</span><br><span class="line">    ctx.socket.emit(<span class="string">'res'</span>, <span class="string">'connected!'</span>);</span><br><span class="line">    <span class="keyword">await</span> next(); <span class="comment">// execute when disconnect.</span></span><br><span class="line">    <span class="built_in">console</span>.log(<span class="string">'disconnection!'</span>);</span><br><span class="line">  &#125;;</span><br><span class="line">&#125;;</span><br></pre></td></tr></table></figure>
<p>Kick out the user example:</p>
<figure class="highlight js"><table><tr><td class="code"><pre><span class="line"><span class="keyword">const</span> tick = <span class="function">(<span class="params">id, msg</span>) =&gt;</span> &#123;</span><br><span class="line">  logger.debug(<span class="string">'# tick'</span>, id, msg);</span><br><span class="line">  socket.emit(id, msg);</span><br><span class="line">  app.io.of(<span class="string">'/'</span>).adapter.remoteDisconnect(id, <span class="literal">true</span>, err =&gt; &#123;</span><br><span class="line">    logger.error(err);</span><br><span class="line">  &#125;);</span><br><span class="line">&#125;;</span><br></pre></td></tr></table></figure>
<p>At the same time, the current connection can also be simple to deal with:</p>
<figure class="highlight js"><table><tr><td class="code"><pre><span class="line"><span class="comment">// &#123;app_root&#125; /app/io/middleware/connection.js</span></span><br><span class="line"><span class="built_in">module</span>.exports = <span class="function"><span class="params">app</span> =&gt;</span> &#123;</span><br><span class="line">  <span class="keyword">return</span> <span class="keyword">async</span> (ctx, next) =&gt; &#123;</span><br><span class="line">    <span class="keyword">if</span> (<span class="literal">true</span>) &#123;</span><br><span class="line">      ctx.socket.disconnect();</span><br><span class="line">      <span class="keyword">return</span>;</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="keyword">await</span> next();</span><br><span class="line">    <span class="built_in">console</span>.log(<span class="string">'disconnection!'</span>);</span><br><span class="line">  &#125;;</span><br><span class="line">&#125;;</span><br></pre></td></tr></table></figure>
<h4 id="packet"><a class="markdown-anchor" href="#packet">#</a> Packet</h4>
<p>Acts on each data packet (each message). In the production environment, it is usually used to preprocess messages, or it is used to decrypt encrypted messages.</p>
<figure class="highlight js"><table><tr><td class="code"><pre><span class="line"><span class="comment">// &#123;app_root&#125; /app/io/middleware/packet.js</span></span><br><span class="line"><span class="built_in">module</span>.exports = <span class="function"><span class="params">app</span> =&gt;</span> &#123;</span><br><span class="line">  <span class="keyword">return</span> <span class="keyword">async</span> (ctx, next) =&gt; &#123;</span><br><span class="line">    ctx.socket.emit(<span class="string">'res'</span>, <span class="string">'packet received!'</span>);</span><br><span class="line">    <span class="built_in">console</span>.log(<span class="string">'packet:'</span>, <span class="keyword">this</span>.packet);</span><br><span class="line">    <span class="keyword">await</span> next();</span><br><span class="line">  &#125;;</span><br><span class="line">&#125;;</span><br></pre></td></tr></table></figure>
<h3 id="controller"><a class="markdown-anchor" href="#controller">#</a> Controller</h3>
<p>A controller deals with the events sent by the client. Since it inherits the <code>egg.controller</code>, it has the following member objects:</p>
<ul>
<li>ctx</li>
<li>app</li>
<li>service</li>
<li>config</li>
<li>logger</li>
</ul>
<blockquote>
<p>For details, refer to the [Controller] (../ basics / controller.md) documentation</p>
</blockquote>
<figure class="highlight js"><table><tr><td class="code"><pre><span class="line"><span class="comment">// &#123;app_root&#125; /app/io/controller/default.js</span></span><br><span class="line"><span class="meta">'use strict'</span>;</span><br><span class="line"></span><br><span class="line"><span class="keyword">const</span> Controller = <span class="built_in">require</span>(<span class="string">'egg'</span>).Controller;</span><br><span class="line"></span><br><span class="line"><span class="class"><span class="keyword">class</span> <span class="title">DefaultController</span> <span class="keyword">extends</span> <span class="title">Controller</span> </span>&#123;</span><br><span class="line">  <span class="keyword">async</span> ping() &#123;</span><br><span class="line">    <span class="keyword">const</span> &#123; ctx, app &#125; = <span class="keyword">this</span>;</span><br><span class="line">    <span class="keyword">const</span> message = ctx.args[<span class="number">0</span>];</span><br><span class="line">    <span class="keyword">await</span> ctx.socket.emit(<span class="string">'res'</span>, <span class="string">`Hi! I've got your message: $ &#123;message&#125;`</span>);</span><br><span class="line">  &#125;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="built_in">module</span>.exports = DefaultController;</span><br><span class="line"></span><br><span class="line"><span class="comment">// or async functions</span></span><br><span class="line"></span><br><span class="line">exports.ping = <span class="keyword">async</span> <span class="function"><span class="keyword">function</span>(<span class="params"></span>) </span>&#123;</span><br><span class="line">  <span class="keyword">const</span> message = <span class="keyword">this</span>.args[<span class="number">0</span>];</span><br><span class="line">  <span class="keyword">await</span> <span class="keyword">this</span>.socket.emit(<span class="string">'res'</span>, <span class="string">`Hi! I've got your message: $ &#123;message&#125;`</span>);</span><br><span class="line">&#125;;</span><br></pre></td></tr></table></figure>
<h3 id="router"><a class="markdown-anchor" href="#router">#</a> Router</h3>
<p>Routing is responsible for passing various events received by the socket to the corresponding controllers.</p>
<figure class="highlight js"><table><tr><td class="code"><pre><span class="line"><span class="comment">// &#123;app_root&#125; /app/router.js</span></span><br><span class="line"></span><br><span class="line"><span class="built_in">module</span>.exports = <span class="function"><span class="params">app</span> =&gt;</span> &#123;</span><br><span class="line">  <span class="keyword">const</span> &#123; router, controller, io &#125; = app; <span class="comment">// default</span></span><br><span class="line">  router.get(<span class="string">'/'</span>, controller.home.index); <span class="comment">// socket.io</span></span><br><span class="line">  io.of(<span class="string">'/'</span>).route(<span class="string">'server'</span>, io.controller.home.server);</span><br><span class="line">&#125;;</span><br></pre></td></tr></table></figure>
<p><strong>note:</strong></p>
<p>Nsp has the following system events:</p>
<ul>
<li><code>disconnecting</code> doing the disconnect</li>
<li><code>disconnect</code> connection has disconnected.</li>
<li><code>error</code> Error occurred</li>
</ul>
<h3 id="namespaceroom"><a class="markdown-anchor" href="#namespaceroom">#</a> Namespace/Room</h3>
<h4 id="namespace-nsp"><a class="markdown-anchor" href="#namespace-nsp">#</a> Namespace (nsp)</h4>
<p>The namespace is usually meant to be assigned to different access points or paths. If the client does not specify a nsp, it is assigned to &quot;/&quot; by default.</p>
<p>In socket.io we use the <code>of</code> to divide the namespace; given that nsp is usually pre-defined and relatively fixed, the framework encapsulates it and uses configuration to partition different namespaces.</p>
<figure class="highlight js"><table><tr><td class="code"><pre><span class="line"><span class="comment">// socket.io</span></span><br><span class="line"><span class="keyword">var</span> nsp = io.of(<span class="string">'/my-namespace'</span>);</span><br><span class="line">nsp.on(<span class="string">'connection'</span>, <span class="function"><span class="keyword">function</span>(<span class="params">socket</span>) </span>&#123;</span><br><span class="line">  <span class="built_in">console</span>.log(<span class="string">'someone connected'</span>);</span><br><span class="line">&#125;);</span><br><span class="line">nsp.emit(<span class="string">'hi'</span>, <span class="string">'everyone!'</span>);</span><br><span class="line"></span><br><span class="line"><span class="comment">// egg</span></span><br><span class="line">exports.io = &#123;</span><br><span class="line">  namespace: &#123;</span><br><span class="line">    <span class="string">'/'</span>: &#123;</span><br><span class="line">      connectionMiddleware: [],</span><br><span class="line">      packetMiddleware: []</span><br><span class="line">    &#125;</span><br><span class="line">  &#125;</span><br><span class="line">&#125;;</span><br></pre></td></tr></table></figure>
<h4 id="room"><a class="markdown-anchor" href="#room">#</a> Room</h4>
<p>Room exists in nsp and is added or left by the join/leave method; the method used in the framework is the same;</p>
<figure class="highlight js"><table><tr><td class="code"><pre><span class="line">Const room = <span class="string">'default_room'</span>;</span><br><span class="line"></span><br><span class="line">Module.exports = <span class="function"><span class="params">app</span> =&gt;</span> &#123;</span><br><span class="line">   <span class="keyword">return</span> <span class="keyword">async</span> (ctx, next) =&gt; &#123;</span><br><span class="line">     ctx.socket.join(room);</span><br><span class="line">     ctx.app.io.of(<span class="string">'/'</span>).to(room).emit(<span class="string">'online'</span>, &#123; <span class="attr">msg</span>: <span class="string">'welcome'</span>, <span class="attr">id</span>: ctx.socket.id &#125;);</span><br><span class="line">     <span class="keyword">await</span> next();</span><br><span class="line">     <span class="built_in">console</span>.log(<span class="string">'disconnection!'</span>);</span><br><span class="line">   &#125;;</span><br><span class="line">&#125;;</span><br></pre></td></tr></table></figure>
<p>**Note: ** Each socket connection will have a random and unpredictable unique id <code>Socket#id</code> and will automatically be added to the room named after this <code>id</code></p>
<h2 id="examples"><a class="markdown-anchor" href="#examples">#</a> Examples</h2>
<p>Here we use <a href="https://github.com/eggjs/egg-socket.io" target="_blank" rel="noopener">egg-socket.io</a> to do a small example which supports p2p chat</p>
<h3 id="client"><a class="markdown-anchor" href="#client">#</a> client</h3>
<p>The UI-related content is not rewritten. It can be called via window.socket</p>
<figure class="highlight js"><table><tr><td class="code"><pre><span class="line"><span class="comment">// browser</span></span><br><span class="line"><span class="keyword">const</span> log = <span class="built_in">console</span>.log;</span><br><span class="line"></span><br><span class="line"><span class="built_in">window</span>.onload = <span class="function"><span class="keyword">function</span>(<span class="params"></span>) </span>&#123;</span><br><span class="line">  <span class="comment">// init</span></span><br><span class="line">  <span class="keyword">const</span> socket = io(<span class="string">'/'</span>, &#123;</span><br><span class="line">    <span class="comment">// Actual use can pass parameters here</span></span><br><span class="line">    query: &#123;</span><br><span class="line">      room: <span class="string">'demo'</span>,</span><br><span class="line">      userId: <span class="string">`client_<span class="subst">$&#123;<span class="built_in">Math</span>.random()&#125;</span>`</span></span><br><span class="line">    &#125;,</span><br><span class="line"></span><br><span class="line">    transports: [<span class="string">'websocket'</span>]</span><br><span class="line">  &#125;);</span><br><span class="line"></span><br><span class="line">  socket.on(<span class="string">'connect'</span>, () =&gt; &#123;</span><br><span class="line">    <span class="keyword">const</span> id = socket.id;</span><br><span class="line"></span><br><span class="line">    log(<span class="string">'#connect,'</span>, id, socket); <span class="comment">// Receive online user information</span></span><br><span class="line"></span><br><span class="line">    socket.on(<span class="string">'online'</span>, msg =&gt; &#123;</span><br><span class="line">      log(<span class="string">'#online,'</span>, msg);</span><br><span class="line">    &#125;); <span class="comment">// Listen for its own id to implement p2p communication</span></span><br><span class="line"></span><br><span class="line">    socket.on(id, msg =&gt; &#123;</span><br><span class="line">      log(<span class="string">'#receive,'</span>, msg);</span><br><span class="line">    &#125;); <span class="comment">// system event</span></span><br><span class="line"></span><br><span class="line">    socket.on(<span class="string">'disconnect'</span>, msg =&gt; &#123;</span><br><span class="line">      log(<span class="string">'#disconnect'</span>, msg);</span><br><span class="line">    &#125;);</span><br><span class="line"></span><br><span class="line">    socket.on(<span class="string">'disconnecting'</span>, () =&gt; &#123;</span><br><span class="line">      log(<span class="string">'#disconnecting'</span>);</span><br><span class="line">    &#125;);</span><br><span class="line"></span><br><span class="line">    socket.on(<span class="string">'error'</span>, () =&gt; &#123;</span><br><span class="line">      log(<span class="string">'#error'</span>);</span><br><span class="line">    &#125;);</span><br><span class="line">  &#125;);</span><br><span class="line"></span><br><span class="line">  <span class="built_in">window</span>.socket = socket;</span><br><span class="line">&#125;;</span><br></pre></td></tr></table></figure>
<h4 id="wechat-applets"><a class="markdown-anchor" href="#wechat-applets">#</a> WeChat Applets</h4>
<p>The API provided by the WeChat applet is WebSocket, and socket.io is the upper encapsulation of Websocket. Therefore, we cannot directly use the API connection of the applet. You can use something like [wxapp-socket-io] (https://github.com/wxsocketio /wxapp-socket-io) to adapt to the library.</p>
<p>The sample code is as follows:</p>
<figure class="highlight js"><table><tr><td class="code"><pre><span class="line"><span class="comment">// Small program-side sample code</span></span><br><span class="line"><span class="keyword">import</span> io <span class="keyword">from</span> <span class="string">'vendor/wxapp-socket-io.js'</span>;</span><br><span class="line"></span><br><span class="line"><span class="keyword">const</span> socket = io(<span class="string">'ws://127.0.0.1:7001'</span>);</span><br><span class="line">socket.on(<span class="string">'connect'</span>, <span class="function"><span class="keyword">function</span>(<span class="params"></span>) </span>&#123;</span><br><span class="line">  socket.emit(<span class="string">'chat'</span>, <span class="string">'hello world!'</span>);</span><br><span class="line">&#125;);</span><br><span class="line">socket.on(<span class="string">'res'</span>, msg =&gt; &#123;</span><br><span class="line">  <span class="built_in">console</span>.log(<span class="string">'res from server: %s!'</span>, msg);</span><br><span class="line">&#125;);</span><br></pre></td></tr></table></figure>
<h3 id="server"><a class="markdown-anchor" href="#server">#</a> server</h3>
<p>The following is part of the demo code and explains the role of each method</p>
<h4 id="config"><a class="markdown-anchor" href="#config">#</a> config</h4>
<figure class="highlight js"><table><tr><td class="code"><pre><span class="line"><span class="comment">// &#123;app_root&#125;/config/config.$&#123;env&#125;.js</span></span><br><span class="line">exports.io = &#123;</span><br><span class="line">  namespace: &#123;</span><br><span class="line">    <span class="string">'/'</span>: &#123;</span><br><span class="line">      connectionMiddleware: [<span class="string">'auth'</span>],</span><br><span class="line">      packetMiddleware: [] <span class="comment">// processing for message is not implemented temporarily</span></span><br><span class="line">    &#125;</span><br><span class="line">  &#125;, <span class="comment">// Data sharing through redis in cluster mode</span></span><br><span class="line"></span><br><span class="line">  redis: &#123;</span><br><span class="line">    host: <span class="string">'127.0.0.1'</span>,</span><br><span class="line">    port: <span class="number">6379</span></span><br><span class="line">  &#125;</span><br><span class="line">&#125;;</span><br></pre></td></tr></table></figure>
<h4 id="helper"><a class="markdown-anchor" href="#helper">#</a> helper</h4>
<p>Framework extensions for encapsulating data formats</p>
<figure class="highlight js"><table><tr><td class="code"><pre><span class="line"><span class="comment">// &#123;app_root&#125;/app/extend/helper.js</span></span><br><span class="line"></span><br><span class="line"><span class="built_in">module</span>.exports = &#123;</span><br><span class="line">  parseMsg(action, payload = &#123;&#125;, metadata = &#123;&#125;) &#123;</span><br><span class="line">    <span class="keyword">const</span> meta = <span class="built_in">Object</span>.assign(</span><br><span class="line">      &#123;&#125;,</span><br><span class="line">      &#123;</span><br><span class="line">        timestamp: <span class="built_in">Date</span>.now()</span><br><span class="line">      &#125;,</span><br><span class="line">      metadata</span><br><span class="line">    );</span><br><span class="line"></span><br><span class="line">    <span class="keyword">return</span> &#123;</span><br><span class="line">      data: &#123;</span><br><span class="line">        action,</span><br><span class="line">        payload</span><br><span class="line">      &#125;,</span><br><span class="line">      meta</span><br><span class="line">    &#125;;</span><br><span class="line">  &#125;</span><br><span class="line">&#125;;</span><br></pre></td></tr></table></figure>
<p>Format：</p>
<figure class="highlight js"><table><tr><td class="code"><pre><span class="line">&#123;</span><br><span class="line">  data: &#123;</span><br><span class="line">    action: <span class="string">'exchange'</span>,  <span class="comment">// 'deny' || 'exchange' || 'broadcast'</span></span><br><span class="line">    payload: &#123;&#125;,</span><br><span class="line">  &#125;,</span><br><span class="line">  meta:&#123;</span><br><span class="line">    timestamp: <span class="number">1512116201597</span>,</span><br><span class="line">    client: <span class="string">'/webrtc#nNx88r1c5WuHf9XuAAAB'</span>,</span><br><span class="line">    target: <span class="string">'/webrtc#nNx88r1c5WuHf9XuAAAB'</span></span><br><span class="line">  &#125;,</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>
<h4 id="middleware-2"><a class="markdown-anchor" href="#middleware-2">#</a> middleware</h4>
<p><a href="https://github.com/eggjs/egg-socket.io" target="_blank" rel="noopener">egg-socket.io</a> middleware handles socket connection handling</p>
<figure class="highlight js"><table><tr><td class="code"><pre><span class="line"><span class="comment">// &#123;app_root&#125;/app/io/middleware/auth.js</span></span><br><span class="line"></span><br><span class="line"><span class="keyword">const</span> PREFIX = <span class="string">'room'</span>;</span><br><span class="line"></span><br><span class="line"><span class="built_in">module</span>.exports = <span class="function"><span class="params">()</span> =&gt;</span> &#123;</span><br><span class="line">  <span class="keyword">return</span> <span class="keyword">async</span> (ctx, next) =&gt; &#123;</span><br><span class="line">    <span class="keyword">const</span> &#123; app, socket, logger, helper &#125; = ctx;</span><br><span class="line">    <span class="keyword">const</span> id = socket.id;</span><br><span class="line">    <span class="keyword">const</span> nsp = app.io.of(<span class="string">'/'</span>);</span><br><span class="line">    <span class="keyword">const</span> query = socket.handshake.query; <span class="comment">// User Info</span></span><br><span class="line"></span><br><span class="line">    <span class="keyword">const</span> &#123; room, userId &#125; = query;</span><br><span class="line">    <span class="keyword">const</span> rooms = [room];</span><br><span class="line"></span><br><span class="line">    logger.debug(<span class="string">'#user_info'</span>, id, room, userId);</span><br><span class="line"></span><br><span class="line">    <span class="keyword">const</span> tick = <span class="function">(<span class="params">id, msg</span>) =&gt;</span> &#123;</span><br><span class="line">      logger.debug(<span class="string">'#tick'</span>, id, msg); <span class="comment">// Send message before kicking user</span></span><br><span class="line"></span><br><span class="line">      socket.emit(id, helper.parseMsg(<span class="string">'deny'</span>, msg)); <span class="comment">// Call the adapter method to kick out the user and the client triggers the disconnect event</span></span><br><span class="line"></span><br><span class="line">      nsp.adapter.remoteDisconnect(id, <span class="literal">true</span>, err =&gt; &#123;</span><br><span class="line">        logger.error(err);</span><br><span class="line">      &#125;);</span><br><span class="line">    &#125;; <span class="comment">// Check if the room exists, kick it out if it doesn't exist // Note: here app.redis has nothing to do with the plugin, it can be replaced by other storage</span></span><br><span class="line"></span><br><span class="line">    <span class="keyword">const</span> hasRoom = <span class="keyword">await</span> app.redis.get(<span class="string">`<span class="subst">$&#123;PREFIX&#125;</span>:<span class="subst">$&#123;room&#125;</span>`</span>);</span><br><span class="line"></span><br><span class="line">    logger.debug(<span class="string">'#has_exist'</span>, hasRoom);</span><br><span class="line"></span><br><span class="line">    <span class="keyword">if</span> (!hasRoom) &#123;</span><br><span class="line">      tick(id, &#123;</span><br><span class="line">        type: <span class="string">'deleted'</span>,</span><br><span class="line">        message: <span class="string">'deleted, room has been deleted.'</span></span><br><span class="line">      &#125;);</span><br><span class="line">      <span class="keyword">return</span>;</span><br><span class="line">    &#125; <span class="comment">// When the user joins</span></span><br><span class="line"></span><br><span class="line">    nsp.adapter.clients(rooms, (err, clients) =&gt; &#123;</span><br><span class="line">      <span class="comment">// Append current socket information to clients</span></span><br><span class="line">      clients[id] = query; <span class="comment">// Join room</span></span><br><span class="line"></span><br><span class="line">      socket.join(room);</span><br><span class="line"></span><br><span class="line">      logger.debug(<span class="string">'#online_join'</span>, _clients); <span class="comment">// Update online user list</span></span><br><span class="line"></span><br><span class="line">      nsp.to(room).emit(<span class="string">'online'</span>, &#123;</span><br><span class="line">        clients,</span><br><span class="line">        action: <span class="string">'join'</span>,</span><br><span class="line">        target: <span class="string">'participator'</span>,</span><br><span class="line">        message: <span class="string">`User(<span class="subst">$&#123;id&#125;</span>) joined.`</span></span><br><span class="line">      &#125;);</span><br><span class="line">    &#125;);</span><br><span class="line"></span><br><span class="line">    <span class="keyword">await</span> next(); <span class="comment">// When the user leaves</span></span><br><span class="line"></span><br><span class="line">    nsp.adapter.clients(rooms, (err, clients) =&gt; &#123;</span><br><span class="line">      logger.debug(<span class="string">'#leave'</span>, room);</span><br><span class="line"></span><br><span class="line">      <span class="keyword">const</span> _clients = &#123;&#125;;</span><br><span class="line">      clients.forEach(<span class="function"><span class="params">client</span> =&gt;</span> &#123;</span><br><span class="line">        <span class="keyword">const</span> _id = client.split(<span class="string">'#'</span>)[<span class="number">1</span>];</span><br><span class="line">        <span class="keyword">const</span> _client = app.io.sockets.sockets[_id];</span><br><span class="line">        <span class="keyword">const</span> _query = _client.handshake.query;</span><br><span class="line">        _clients[client] = _query;</span><br><span class="line">      &#125;);</span><br><span class="line"></span><br><span class="line">      logger.debug(<span class="string">'#online_leave'</span>, _clients); <span class="comment">// Update online user list</span></span><br><span class="line"></span><br><span class="line">      nsp.to(room).emit(<span class="string">'online'</span>, &#123;</span><br><span class="line">        clients: _clients,</span><br><span class="line">        action: <span class="string">'leave'</span>,</span><br><span class="line">        target: <span class="string">'participator'</span>,</span><br><span class="line">        message: <span class="string">`User(<span class="subst">$&#123;id&#125;</span>) leaved.`</span></span><br><span class="line">      &#125;);</span><br><span class="line">    &#125;);</span><br><span class="line">  &#125;;</span><br><span class="line">&#125;;</span><br></pre></td></tr></table></figure>
<h4 id="controller-2"><a class="markdown-anchor" href="#controller-2">#</a> controller</h4>
<p>Data exchange of P2P communication is through exchange</p>
<figure class="highlight js"><table><tr><td class="code"><pre><span class="line"><span class="comment">// &#123;app_root&#125;/app/io/controller/nsp.js</span></span><br><span class="line"><span class="keyword">const</span> Controller = <span class="built_in">require</span>(<span class="string">'egg'</span>).Controller;</span><br><span class="line"></span><br><span class="line"><span class="class"><span class="keyword">class</span> <span class="title">NspController</span> <span class="keyword">extends</span> <span class="title">controller</span> </span>&#123;</span><br><span class="line">  <span class="keyword">async</span> exchange() &#123;</span><br><span class="line">    <span class="keyword">const</span> &#123; ctx, app &#125; = <span class="keyword">this</span>;</span><br><span class="line">    <span class="keyword">const</span> nsp = app.io.of(<span class="string">'/'</span>);</span><br><span class="line">    <span class="keyword">const</span> message = ctx.args[<span class="number">0</span>] || &#123;&#125;;</span><br><span class="line">    <span class="keyword">const</span> socket = ctx.socket;</span><br><span class="line">    <span class="keyword">const</span> client = socket.id;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">try</span> &#123;</span><br><span class="line">      <span class="keyword">const</span> &#123; target, payload &#125; = message;</span><br><span class="line">      <span class="keyword">if</span> (!target) <span class="keyword">return</span>;</span><br><span class="line">      <span class="keyword">const</span> msg = ctx.helper.parseMsg(<span class="string">'exchange'</span>, payload, &#123; client, target &#125;);</span><br><span class="line">      nsp.emit(target, msg);</span><br><span class="line">    &#125; <span class="keyword">catch</span> (error) &#123;</span><br><span class="line">      app.logger.error(error);</span><br><span class="line">    &#125;</span><br><span class="line">  &#125;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="built_in">module</span>.exports = NspController;</span><br></pre></td></tr></table></figure>
<h4 id="router-2"><a class="markdown-anchor" href="#router-2">#</a> router</h4>
<figure class="highlight js"><table><tr><td class="code"><pre><span class="line"><span class="comment">// &#123;app_root&#125;/app/router.js</span></span><br><span class="line"><span class="built_in">module</span>.exports = <span class="function"><span class="params">app</span> =&gt;</span> &#123;</span><br><span class="line">  <span class="keyword">const</span> &#123; router, controller, io &#125; = app;</span><br><span class="line">  router.get(<span class="string">'/'</span>, controller.home.index); <span class="comment">// socket.io</span></span><br><span class="line"></span><br><span class="line">  io.of(<span class="string">'/'</span>).route(<span class="string">'exchange'</span>, io.controller.nsp.exchange);</span><br><span class="line">&#125;;</span><br></pre></td></tr></table></figure>
<p>Open two tab pages and call up the console:</p>
<figure class="highlight js"><table><tr><td class="code"><pre><span class="line">socket.emit(<span class="string">'exchange'</span>, &#123;</span><br><span class="line">  target: <span class="string">'/webrtc#Dkn3UXSu8_jHvKBmAAHW'</span>,</span><br><span class="line">  payload: &#123;</span><br><span class="line">    msg: <span class="string">'test'</span></span><br><span class="line">  &#125;</span><br><span class="line">&#125;);</span><br></pre></td></tr></table></figure>
<p><img src="https://raw.githubusercontent.com/eggjs/egg/master/docs/assets/socketio-console.png" alt=""></p>
<h2 id="reference-links"><a class="markdown-anchor" href="#reference-links">#</a> Reference Links</h2>
<ul>
<li><a href="https://socket.io" target="_blank" rel="noopener">socket.io</a></li>
<li><a href="https://github.com/eggjs/egg-socket.io" target="_blank" rel="noopener">egg-socket.io</a></li>
<li><a href="https://github.com/eggjs/egg-socket.io/tree/master/example" target="_blank" rel="noopener">egg-socket.io example</a></li>
</ul>

  </article>
  <aside id="mobileAside" class="toc">
  <div class="mobile-menu">
    <ul>
      <li><a href="/en/intro/" alt="Guide">Guide</a></li><li><a href="/api/" alt="API">API</a></li><li><a href="/en/tutorials/index.html" alt="Tutorials">Tutorials</a></li><li><a href="https://github.com/search?q=topic%3Aegg-plugin&type=Repositories" alt="Plugins">Plugins</a></li><li><a href="https://github.com/eggjs/egg/releases" alt="Release">Release</a></li>
      
      
        <li class="translations">
          <a class="nav-link">中文文档</a>
          <span class="arrow"></span><ul id="dropdownContent" class="dropdown-content"><li><a id="en" href="/en/tutorials/socketio.html" style="color: #22ab28">English</a></li><li><a id="zh-cn" href="/zh-cn/tutorials/socketio.html" >中文</a></li></ul>
        </li>
      
    </ul>
  </div>
  <dl><dt id="title-Intro" style="cursor: pointer;" class="aside-title">Guide<a id="collapse-icon-Intro" class="icon opend"></a></dt><dd id=panel-Intro><ul><li><a href="/en/intro/index.html" class="menu-link">What is Egg?</a></li><li><a href="/en/intro/egg-and-koa.html" class="menu-link">Egg and Koa</a></li><li><a href="/en/intro/quickstart.html" class="menu-link">Quick Start</a></li><li><a href="/en/tutorials/progressive.html" class="menu-link">Progressive</a></li><li><a href="/en/migration.html" class="menu-link">Migration to 2.x</a></li></ul></dd><dt id="title-Basics" style="cursor: pointer;" class="aside-title">Basis Function<a id="collapse-icon-Basics" class="icon opend"></a></dt><dd id=panel-Basics><ul><li><a href="/en/basics/structure.html" class="menu-link">Directory Structure</a></li><li><a href="/en/basics/objects.html" class="menu-link">Built-in Objects</a></li><li><a href="/en/basics/env.html" class="menu-link">Environment</a></li><li><a href="/en/basics/config.html" class="menu-link">Configuration</a></li><li><a href="/en/basics/middleware.html" class="menu-link">Middleware</a></li><li><a href="/en/basics/router.html" class="menu-link">Router</a></li><li><a href="/en/basics/controller.html" class="menu-link">Controller</a></li><li><a href="/en/basics/service.html" class="menu-link">Service</a></li><li><a href="/en/basics/plugin.html" class="menu-link">Plugin</a></li><li><a href="/en/basics/schedule.html" class="menu-link">Schedule</a></li><li><a href="/en/basics/extend.html" class="menu-link">Extend</a></li><li><a href="/en/basics/app-start.html" class="menu-link">Custom Init</a></li></ul></dd><dt id="title-Core" style="cursor: pointer;" class="aside-title">Core<a id="collapse-icon-Core" class="icon opend"></a></dt><dd id=panel-Core><ul><li><a href="/en/core/development.html" class="menu-link">Development</a></li><li><a href="/en/core/unittest.html" class="menu-link">Unit Testing</a></li><li><a href="/en/core/deployment.html" class="menu-link">Deployment</a></li><li><a href="/en/core/logger.html" class="menu-link">Logger</a></li><li><a href="/en/core/httpclient.html" class="menu-link">HttpClient</a></li><li><a href="/en/core/cookie-and-session.html" class="menu-link">Cookie and Session</a></li><li><a href="/en/core/cluster-and-ipc.html" class="menu-link">Cluster and IPC</a></li><li><a href="/en/core/view.html" class="menu-link">View</a></li><li><a href="/en/core/error-handling.html" class="menu-link">Error Handling</a></li><li><a href="/en/core/security.html" class="menu-link">Security</a></li><li><a href="/en/core/i18n.html" class="menu-link">i18n</a></li></ul></dd><dt id="title-Tutorials" style="cursor: pointer;" class="aside-title">Tutorials<a id="collapse-icon-Tutorials" class="icon opend"></a></dt><dd id=panel-Tutorials><ul><li><a href="/en/tutorials/mysql.html" class="menu-link">MySQL</a></li><li><a href="/en/tutorials/restful.html" class="menu-link">RESTful API</a></li><li><a href="/en/tutorials/passport.html" class="menu-link">Passport</a></li><li><a href="/en/tutorials/socketio.html" class="menu-link">Socket.IO</a></li><li><a href="/en/tutorials/assets.html" class="menu-link">Assets</a></li><li><a href="/en/tutorials/typescript.html" class="menu-link">TypeScript</a></li></ul></dd><dt id="title-Advanced" style="cursor: pointer;" class="aside-title">Advanced<a id="collapse-icon-Advanced" class="icon opend"></a></dt><dd id=panel-Advanced><ul><li><a href="/en/advanced/loader.html" class="menu-link">Loader</a></li><li><a href="/en/advanced/plugin.html" class="menu-link">Plugin Development</a></li><li><a href="/en/advanced/framework.html" class="menu-link">Framework</a></li><li><a href="/en/advanced/cluster-client.html" class="menu-link">Cluster Enhancement</a></li><li><a href="/en/advanced/view-plugin.html" class="menu-link">View Plugin</a></li><li><a href="/en/style-guide.html" class="menu-link">Style Guide</a></li></ul></dd><dt id="title-Community" style="cursor: pointer;" class="aside-title">Community<a id="collapse-icon-Community" class="icon opend"></a></dt><dd id=panel-Community><ul><li><a href="/en/plugins/" class="menu-link">Plugin List</a></li><li><a href="/en/contributing.html" class="menu-link">Contributing</a></li><li><a href="/en/resource.html" class="menu-link">Resource</a></li><li><a href="/en/faq.html" class="menu-link">FAQ</a></li></ul></dd></dl>
</aside>
<script>
var mobileTrigger = document.getElementById('mobileTrigger');
var mobileAside = document.getElementById('mobileAside');

var expandMenu = function(title) {
  // handle icon
  const collapseIcon = document.getElementById('collapse-icon-' + title);
  if (collapseIcon) {
    collapseIcon.className = 'icon opend';
  }
  // handle panelEle
  const panelEle = document.getElementById('panel-' + title);
  if (panelEle) {
    panelEle.className = '';
  }
}

var collapseMenu = function(title) {
  // handle icon
  const collapseIcon = document.getElementById('collapse-icon-' + title);
  if (collapseIcon) {
    collapseIcon.className = 'icon closed';
  }
  // handle panelEle
  const panelEle = document.getElementById('panel-' + title);
  if (panelEle) {
    panelEle.className = 'aside-panel-hidden';
  }
}

mobileAside.onclick = function(e) {
  const targetId = e.target.id;
  if (targetId && (targetId.indexOf('title-') > -1 || targetId.indexOf('collapse-icon-') > -1)) {
    const title = targetId.replace('title-', '').replace('collapse-icon-', '');
    try { 
      // the the browser may have no localStroage or JSON.parse may throw exception.
      const menuInfo = JSON.parse(window.localStorage.getItem('menuInfo'));
        
      // current menu status
      const curClosed = menuInfo[title] ? menuInfo[title].closed : false; // default false

      // change UI
      curClosed ? expandMenu(title) : collapseMenu(title);

      // save menuInfo to localStorage
      menuInfo[title] = { closed: !curClosed } // opposite
      window.localStorage.setItem('menuInfo', JSON.stringify(menuInfo));
    } catch (e) {}
  }
};

mobileTrigger.onclick = function(e) {
  e.preventDefault();
  if (mobileAside.className.indexOf('mobile-show') === -1) {
    mobileAside.className += ' mobile-show';
  } else {
    mobileAside.className = 'toc';
  }
};

(function() {
  // save data to localStorage because the page will refresh when user change the url.
  let menuInfo;
  try { 
    // the the browser may have no localStroage or JSON.parse may throw exception.
    menuInfo = JSON.parse(window.localStorage.getItem('menuInfo'));
    if (!menuInfo) {
      menuInfo = {};
      window.localStorage.setItem('menuInfo', JSON.stringify(menuInfo));
    }
  } catch (e) {
    menuInfo = {}; // default {}
  }

  for (const title in menuInfo) {
    if (menuInfo[title] && menuInfo[title].closed) { // menu in closed status.
      collapseMenu(title);
    } else {
      expandMenu(title);
    }
  }

  // highlight menu
  const pathname = window.location.pathname;
  const selector = `a[href="${pathname}"].menu-link,a[href="${pathname}index.html"].menu-link`;
  const menuItem = mobileAside.querySelector(selector);
  if (menuItem) { menuItem.className += ' highlight'; }
})();
</script>

</div>

  </div>
</body>
<script src="https://cdn.jsdelivr.net/docsearch.js/2/docsearch.min.js"></script>
<script>
docsearch({
  apiKey: '1561de31a86f79507ea00cdb54ce647c',
  indexName: 'eggjs',
  inputSelector: '#search-query',
});
</script>
<div class="cnzz">
<script src="https://s11.cnzz.com/z_stat.php?id=1261142226&web_id=1261142226" language="JavaScript"></script>
</div>

</html>
